Software C & C++ Coding Standards


                             ~ Scope ~

Software source code shall conform to these standards.  These standards
are derived from common coding practices used in commercial software
development.  These standards shall be used critique code in the formal
review process.  

                       ~ Coding Standards ~

1.0  Specification/Implementation:

1.1  Software shall be designed and implemented in a modular architecture.
A software module is defined as a C file (*.c) or a C++ class 
(*.cc or *.cpp).  A module shall have an explicit public interface.  
A module shall have a specification which defines the data and behavior
of the module.  The specification for a module shall be in the form of
a *.h file, for a *.c file and *.hh file, for a *.cc or *.cpp file.

1.2  The specification file shall include all *.h and *.hh files
required by the source implementation.  The implementation file shall
include only one *.h or *.hh which is the specification for the
implementation.

  Figure 1.0 Specification/Implementation Example

                  Specification   
        C                             C++

  /* motor.h */                // motor.hh
  #ifndef MOTOR_H              #ifndef MOTOR_HH
  #define MOTOR_H              #define MOTOR_HH

  #ifdef __cplusplus
  extern "C" {
  #endif

  #include <stdio.h>           #include <iostream.h>
  #include <stdlib.h>          #include <stdlib.h>
  #include "parts.h"           #include "parts.hh"
  
  #define MAXMPH 85            const int MAXMPH = 85;
  extern int fuel;

  int start ( void );          class Motor
  int accel ( int rate );      {
  int stop  ( void );            private: 
                                   int fuel;
  #ifdef __cplusplus             public:
  }                                Motor() { fuel = 0; }
  #endif                           int start ( void );
                                   int accel ( void );
  #endif                           int stop  ( void ); 
  /* eof motor.h */                ~Motor(){
                                };
                               // eof motor.hh
                              

                  Implementation

        C                             C++

  /* motor.c */               // motor.cc
  #include "motor.h"          #include "motor.hh"

  int                         int
  start ( void )              Motor::start ( void )
  {                           {
    fuel = 10;                  fuel = 10;
  }                           }

  int                         int
  accel ( int rate )          Motor::accel ( int rate )
  {                           {
    fuel = rate;                fuel = rate;
  }                           }

  int                         int
  stop ( void )               Motor::stop ( void )
  {                           {
    fuel = 0;                   fuel = 0;
  }                           }
  /* eof motor.c */           // eof motor.cc


2.0  Independent Compilation

2.1  A module shall be completely defined by it's specification.  Client (consumer)
modules which need to use a server (producer) module's implementation shall
include the specification for the server module.  A module shall be capable
of being compiled without dependencies on other modules.  The output of the
compiling phase shall be object files for each compiled module.

  C Compilation               C++ Compilation

  gcc -c motor.c              gcc -c motor.cc


3.0  Module Coupling and Global Data

3.1  Modules shall be as loosely coupled as possible.  This requirement mandates
the minimization of global data and to maximize the encapsulation data within
a module.  All class data shall be private with public accessor or member functions.
For global data global data shall be encapsulated within a structure.

3.2  Functions shall receive all external data through the function (public) interface
or through the encapsulation of the class.  The use of global data shall be
explicitly denoted by the use of the structure binding.

3.3  Binding the global data within a structure, reduces the possibility of name space 
collisions with other global data.  The name of the should explicitly detail the
data as global.  Global data shall be declared and initialized in the main module.
All other use of the global data shall be through the use of external references.
Global references shall be defined in a file indicated as global.

  Example of global data:

  /* main.c */
  #include "global.h"
  #include "motor.h"

  /* create global data */
  global_MotorType Gbl_Motor;

  int
  main ( void )
  {
    /* using global data &/
    Gbl_Motor.fuel = 23;
    strcpy(Gbl_Motor.data,"04301997");
  
    ...

  }
  /* eof main.c */


  /* global.h */
  #ifndef GLOBAL_H
  #define GLOBAL_H

  #ifdef __cplusplus
  extern "C" {
  #endif
  
  typedef struct
  {
    int fuel;
    char date[9];
  } global_MotorType;
  
  extern global_MotorType Gbl_Motor;

  #ifdef __cplusplus
  }
  #endif

  #endif
  /* eof global.h */

3.4  The compiler will identify undefined symbols or duplicate symbols at
compile time.  Undefined symbols are symbols which have not been included into
the compiled source.  Using the appropriate include files or libraries are
required to resolve undefined symbols.  Duplicate symbols are usually the
result of not using #ifdef ... #endif wrappers around header files.


4.0  Scoping Rules

4.1  Scope rules define the visibility of an object.  An object can have global, file,
function and local (block) scope.  Global scope was previously covered in the previous
section.  

4.2  File scope is defined by using the key word static.  Objects declared
static are only visible in the file for which they are defined.

  File Scope Example:

  #define MAXCOLORS 16
  static char * PixelColor[MAXCOLORS] = 
    { "BLACK","WHITE","BLUE","GREEN","RED","ORANGE",
      "VIOLET","CYAN","GREY","PURPLE","PINK","BROWN",
      "LTGREEN","LTBLUE","NONE" };

4.3  Function scope is defined within the opening and closing braces of a function.
C syntax requires that data be declared at the beginning of the scope before
and executable statements.  C++ allows the programmer the flexibility to declare
an object anywhere in the scope.  Special care must be taken to ensure that
an objects initialization and use is after it's declaration within the function
scope.

4.4  Local or block scope is associated with the open and close braces.  These
braces are also declare scope for "if", "else", "while", "for", "switch" 
and "case" statements.  Local scope shall be used to create code which is
easily read and maintained.  

4.5  Braces shall be used on a single line to denote a change in scope.  All code 
and comments bound to a scope shall be indented 2 spaces.  Reference the
following example as the proper pro-grammatic scoping.

  int
  Read_Line ( char * line )
  {
    int index = 0;

    for ( index = 0; index < strlen(line); index++ )
    {
      printf ( "%c", line[index] );
    }

    printf ( "\n" );
  } 

4.6  All "if" and "else" statements shall be delimited by scope.  Stand-alone "if"
statements are prone to logic errors during maintenance.  Code can inadvertently
fall outside of a conditional block.  The following example illustrates how
inserting a debug statement in the wrong place during maintenance can cause
the program to ALWAYS exit regardless of the value of "x".

  if ( x < 10 )
    printf ( "DEBUG: Exiting on x = %d\n", x );
    exit(1);
  printf ( "DEBUG: Continuing...\n" );  

  The correct method for scope the previous "if" statement is shown below.

  if ( x < 10 )
  { 
    printf ( "DEBUG: Exiting on x = %d\n", x );
    exit(1);
  }
  printf ( "DEBUG: Continuing...\n" );  

  Local scope for "switch", "case" and "default" can make code easier to read
  and to me maintained.

  switch ( c )
  {
    case 'A':
    {
      Execute_A();
    }
    break;

    case 'B':
    {
      Execute_B();
    }
    break;

    default:
    {
      printf ( "Error: %c unsupported\n", c );
    }
  }

4.7  Local scope can be used anywhere to denote a block of execution.  All code
within this block must be indented 2 spaces.  Local scope maybe used to 
declare variable anywhere with a function.

  void
  Show ( void )
  {
    int i = 0;
    int x = 10;
    for ( i=0; i<x; i++ );
    {
      { /* local scope for declaring
           local variable */
        int z = 22;
        printf ( "%d\n", z*i );
      }
    }
  }

4.8  All compilers will identify unbalanced scope braces.  The programmer
must design and defines the scope for all objects used in the program.
Objects defined within a scope will automatically be destroyed when the 
object drops out of scope.  C++ objects defined within local scope are 
created on the stack.  Stack based objects which drop out of scope will 
automatically fire their destructor.  Heap based objects or dynamically 
created objects have a life time defined by the programmer.  These objects
must be explicitly destroyed by the 


5.0 Logic or Program Flow

5.1  The key word 'goto' and associated labels shall not be used within
the code.  This type of architecture creates logic flow which jumps from
label to label.  It is very difficult to follow the sequential flow of
the program when the 'goto' jumps to a label defined within another file. 
As the key word 'goto' is a keyword in C and C++, compilers will not 
identify the problematic used of this construct.

5.2  Every 'switch' conditional shall have a 'default' label.  
The default condition will catch all cases not explicitly defined
within the 'switch' construct.  Compilers will not identify the
failure of placing a 'default' condition for a switch condition.

5.3  Every 'case' or set of 'case' conditions shall have a 'break'
statement.  The 'break' statement will terminate the 'case' condition
instruction set.  Failure to place a 'break' statement after 'case'
scope will cause the program flow to drop through to the next 'case'
condition.  Compilers will not identify case conditions which do not
have a 'break' statement.

5.4  Every if and while condition shall best test to ensure that
an assignment is not used in place of an equivalence test.
Placing an assignment instead of an equivalence test for an
'if' and 'while' condition will create erroneous logic flow.  Most
compilers will not identify this type of semantic error.

  Assignment        vs.        Equivalence Test
   (error)

  if ( x = 1 )                if ( x == 1 )
  {                           {
    ...                         ...
  }                           }

5.5  Functions other than 'main' shall not use the functions
'exit or '_exit'.  The use of 'exit' or '_exit' can prematurely 
terminate a program.  Programs should be designed to gracefully 
return from each function and spiral out of the function stack 
completely to the operating system.  This type of design will 
enable the use of exception handling, if not by the language, 
then by the operating system.  Ideally each module or function 
should contain one entry point and one exit point.


6.0 Maintainability

6.1  Functions shall not use variable argument lists or ellipses.
Functions with variable argument lists do not allow the
compiler to check the type and number of arguments passed to
the function.  This type of design should be eliminated to
ensure discrete modular interfaces.

6.2  All functions shall use ANSI prototyping.  The use on 
Non-ANSI function prototyping will disable compile time type
and number of argument testing.  Many compilers allow transitional
code or code which combines a mixture of K&R and ANSI C.  The
compiler flags must be set to compile strict ANSI code.

6.3  C++ keywords shall not be used as symbols in C programs.
The use of C++ keywords in C programs can prevent the
C functions or programs from being recompiled with a C++
compiler.  A C compiler will not warn the programmer of this
potential conflict.  For example the following C function will
not compile correctly with a C++ compiler, but it will compile
cleanly with a C compiler.

  void
  Some_C_Function ( void )
  {
    int class = 0;  /* class is a keyword in C++ but valid
                       as a symbol in C */
    /* ... */
  }
  
6.4  Friend classes shall not be used in C++ source code.
when a class befriends another class, there exists latent 
access to all members if the friend class.  This type of
access breaks the encapsulation of the friend class.

6.5  The creation of dynamic memory shall be checked to
insure that the creation was successful.

  /* C Implementation */
  char * mem_pointer = ( char *) malloc ( 100 );
  if ( !mem_pointer )
  {
    perror ( "memory allocation error!" );
    return (-1);
  }

  // C++ Implementation
  Vertex * object_ptr = new Vertex( 1,2,3 );
  if ( !object_ptr )
  {
    perror ( "memory allocation error!" );
    return (-1);
  }

6.6  All dynamic memory pointers shall be checked for
NULL before a 'free'or 'delete' is executed on the pointer.
All 'realloc' call must insure that and latent free will
not occur from a 'realloc' size of zero.

  C Implementation              C++ Implementation

  if ( mem_pointer )            if ( object_ptr )
  {                             {
    free ( mem_pointer );         delete ( object_ptr );
    mem_pointer = NULL;           object_ptr = NULL;
  }                             }

  if ( size > 0 )
  {
    realloc ( mem_pointer, size );
  }

6.7  The lines of code, LOC, for a function should be kept to
a nominal level of 200 lines.  Function exceeding this LOC are
difficult to navigate and maintain.  Large functions will require
more testing than simple small functions.  The ideal function
is small and of single purpose.

6.8  Source code lines shall be less than or equal to 80 characters
in width.  This measurement is compliant with the standard terminal
width.  Code written to the standard terminal width will display
and/or print correctly.

6.9  The use of the pre/post increment/decrement operators shall
be limited to single purpose statements.  The use of pre-increment
and pre-decrement operators is problematic when used in compound
statements.  It is difficult to predict the precedence of operations
when operators are compounded.

  /* confusing code */

  *++p = *--p + 1;

  Nextptr = *p+++1;
    
6.10  The use of in-line functions shall be used over macro calls.
Macros are substituted at compile time and have no context in a 
symbolic debugger.  Macros are extremely difficult to debug and
are difficult to code correctly.

6.11  The use of constants created with the ANSI key word 'const'
shall be used instead of #define constants.  Constants declared
with '#define' do not comply with the C or C++ language syntax
and have no context within the symbolic debugger.  The compiler
cannot check the type of '#define' constants.

6.12  The use of the '?' operator shall be restricted to macros.
The '?' operator implies and 'if - else' construct.  It is 
better to use an 'if - else' construct in normal code 
implementation.

6.13  Configuration control or copyright marks are usually
required within each code file.  Each file shall contain the
required configuration and/or copyright statements.

6.14  Each function and file shall contain at least 10 percent white
space.  White space creates code which is easy to read and
understand.

6.15  Each function and file shall contain at least 10 percent
comments.  Comments are essential for the maintenance of 
source code modules.

6.16  Function names shall be descriptive to the nature of
the function.  The use of mixed case and underscores shall
be used to create multi-word function names.

  void ShowTheVertex ( void );

  int Set_X ( void );

6.17  User defined type names shall be in proper case.  This
includes all typedefs, classes and structures.

  class Vertex {};

  typedef struct
  {
    int x;
    int y;
    int z;
  } CoordinateType;

6.18  Symbolic constants shall be used for all numeric constants.
These constants shall be defined in the appropriate header files.
Symbolic constants shall be named to convey the intent of use.
Symbolic constants shall be named in upper case.

  /* specification file */
  const double PI = 3.14159;
  const int NAMESIZE = 11;

  /* implementation file */
  char name[NAMESIZE];


